//go:build ignore
// +build ignore

package main

import (
	"fmt"
	"net"
	"strings"
	"time"
)

// 每个客户端的结构体
type Client struct {
	info    string      // ip port
	name    string      // 用户可以自己设置的用户名 初始为info
	msgChan chan string // 用户用于发送信息的管道
}

// 格式化打印到公屏的字符串
func formatMsg(from string, msg string) (fmsg string) {
	fmsg = fmt.Sprintf("[%s]:\t%s\n", from, msg)
	return
}

// 对于每个客户端，有信息就打印
func clientMsg(conn net.Conn, msgChan chan string) {
	for msg := range msgChan {
		conn.Write([]byte(msg))
	}
	close(msgChan)
}

// 每个连接需要做的事情
func connect(conn net.Conn, globalMsg chan string, allClients map[string]Client) {
	defer conn.Close()
	// 创建该客户端实例
	info := conn.RemoteAddr().String()
	cli := Client{info, info, make(chan string)}
	// 注册进map中
	allClients[info] = cli
	// 广播登录信息
	globalMsg <- formatMsg(info, "login")
	// 如果管道有信息，需要打印
	go clientMsg(conn, cli.msgChan)
	// 监听客户端输入的信息
	// 改为协程，以便添加超时断线功能
	infoChan := make(chan string)
	toExit := make(chan bool) // 下面的函数变成协程后return不能结束connect函数了，需要一个新的标志
	go func() {
		for {
			msg_b := make([]byte, 1024*4)
			n, err := conn.Read(msg_b)
			if err != nil {
				// fmt.Println("conn.Read err =", err)
				// toExit <- true
				return
			}
			msg := string(msg_b[:n-1])
			// 添加有输入信息的标志
			infoChan <- msg
			if msg == "who" {
				// 查询当前有哪些客户端在线
				conn.Write([]byte("--------------------------------------\n"))
				conn.Write([]byte("client list:\n"))
				for _, c := range allClients {
					conn.Write([]byte(fmt.Sprintf("[%s]:%s\n", c.info, c.name)))
				}
				conn.Write([]byte("--------------------------------------\n"))
			} else if len(msg) > 7 && msg[:6] == "rename" {
				// 修改用户名
				slice := strings.Fields(msg)
				old_name := cli.name
				new_name := slice[1]
				cli.name = new_name
				allClients[cli.info] = cli
				conn.Write([]byte(fmt.Sprintf("rename from %s to %s\n", old_name, new_name)))
			} else if msg == "exit" {
				// 退出
				globalMsg <- formatMsg(cli.name, "*"+msg+" logout*")
				// 删除该客户端
				delete(allClients, cli.info)
				toExit <- true
				return
			} else {
				// 正常广播
				globalMsg <- formatMsg(cli.name, msg)
			}
		}
	}()
	// 延迟断线功能
	for {
		select {
		case <-infoChan:
			continue
		case <-time.After(time.Second * 30):
			// 超时
			globalMsg <- formatMsg(cli.name, "*time out logout*")
			// 删除该客户端
			delete(allClients, cli.info)
			return
		case <-toExit:
			// 删除该客户端
			delete(allClients, cli.info)
			return
		}
	}
}

// 对于每个发送到globalMsg管道的信息，对全部在线客户端进行广播
func processMsg(globalMsg chan string, allClients map[string]Client) {
	for msg := range globalMsg {
		for _, cli := range allClients {
			cli.msgChan <- msg
		}
	}
	close(globalMsg)
}

func main() {
	globalMsg := make(chan string)
	allClients := make(map[string]Client)

	// 获取监听
	listener, err := net.Listen("tcp", ":8000")
	if err != nil {
		fmt.Println("net.Listen err =", err)
		return
	}
	defer listener.Close()

	// 处理收到的信息的协程（进行广播）
	go processMsg(globalMsg, allClients)

	// 对每个请求建立连接
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("listener.Accept err =", err)
			continue
		}
		go connect(conn, globalMsg, allClients)
	}
}
